import re
import json
import logging

from django.core.urlresolvers import reverse
from django.shortcuts import render, redirect
from django.db import DatabaseError
from django.contrib.auth import login, authenticate, logout
from django.http import HttpResponse, HttpResponseForbidden, JsonResponse # HttpResponseForbidden 里面封装的有403响应
from django.views.generic import View
from django_redis import get_redis_connection
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django import http
from django.contrib.auth.decorators import login_required

from urtils.view import LoginRequiredJSONMixin, LoginRequiredMixin
from meiduo.models import Users, Address
from urtils.response_code import RETCODE
from celery_tasks.email.tasks import send_verify_email
from meiduo.urtil import generate_verify_email_url, check_verify_email_token
from meiduo import constants
from carts.utils import marge_carts_cookie_redis
from goods.models import SKU


# 添加日志输出器
logger = logging.getLogger("django")


class UserBrowseHistory(LoginRequiredJSONMixin, View):
    """用户浏览记录"""

    def post(self, request):
        # 获取数据
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        sku_id = json_dict.get('sku_id')

        # 验证数据
        try:
            SKU.objects.get(id=sku_id)
        except ObjectDoesNotExist:
            return http.HttpResponseForbidden("sku_id错误")

        # 保存sku_id到redis
        redis_coon = get_redis_connection('history')
        user = request.user
        # 管道
        pl = redis_coon.pipeline()
        # 先去重
        # lrem(命名，count, 值)
        # count > 0 : 从表头开始向表尾搜索，移除与 value 相等的元素，数量为 count 。
        # count < 0 : 从表尾开始向表头搜索，移除与 value 相等的元素，数量为 count 的绝对值。
        # count = 0 : 移除表中所有与 value 相等的值。
        pl.lrem('history_%s' % user.id, 0, sku_id)
        # 在保存
        pl.lpush('history_%s' % user.id, sku_id)
        # 最后截取
        # ltrim(命名，start, stop)
        pl.ltrim('history_%s' % user.id, 0, 4)
        # 执行
        pl.execute()
        # 返回数据
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'Ok'})

    def get(self, request):
        """查询商品浏览记录"""
        # 获取用户登录信息
        user = request.user
        # 连接到 redis 数据库
        redis_coon = get_redis_connection('history')
        # 查询 redis 数据库里的列表数据　（核心代码）
        sku_ids = redis_coon.lrange('history_%s' % user.id, 0, -1)
        # 将模型转为字典
        skus = []
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id)
            skus.append({
                'id': sku.id,
                'name': sku.name,
                'price': sku.price,
                'default_image_url': settings.FDFS_BASE_URL+sku.default_image
            })
        # 响应数据
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok', "skus": skus})


class UpdateTitleAddressView(LoginRequiredJSONMixin, View):
    """修改地址标题"""
    def put(self, request, address_id):
        # 接受数据
        address_dict = json.loads(request.body.decode())
        title = address_dict.get('title')
        try:
            # 查询地址
            address = Address.objects.get(id=address_id)
            # 修改标题
            address.title = title
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({"code": RETCODE.DBERR, 'errmsg': '修改标题失败'})
        # 返回结果
        return http.JsonResponse({"code": RETCODE.OK, 'errmsg': '修改标题成功'})

class DefaultAddressVIew(LoginRequiredJSONMixin, View):
    """设置默认地址"""
    def put(self, request, address_id):
        try:
            """获取要修改地址的信息id"""
            address = Address.objects.get(id=address_id)
            request.user.default_address = address
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({"code": RETCODE.DBERR, "errmsg": "设置默认地址失败"})
        # 响应结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})


class UpdateDestroyAddressView(LoginRequiredJSONMixin, View):
    """更新和删除地址"""
    def put(self, request, address_id):
        # 接收地址
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        # 新的地址替换为旧的地址
        # 判断地址是否存在
        try:
            Address.objects.filter(id=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '地址数量修改失败'})

        # 新的数据返回前端渲染
        address = Address.objects.get(id=address_id)
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        # 响应更新地址结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'address': address_dict})

    def delete(self, request, address_id):
        """删除数据"""
        # 获取要删除的id
        try:
            address = Address.objects.get(id=address_id)
            address.is_deleted = True
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功'})

class AddressCreateView(LoginRequiredJSONMixin, View):
    """新增地址"""

    def post(self, request):
        """实现业务逻辑"""
        # 判断用户地址是否超过20
        count = request.user.addresses.count()
        if count > constants.USER_ADDRESS_COUNTS_LIMIT:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '地址数量超上线'})

        # 接受数据
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        # 实现业务
        try:
            address = Address.objects.create(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email,
            )
            # 如果用户没有默认地址，默认指定默认地址
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()

        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR,'errmsg': '增加地址失败'})

            # 新增地址成功，将新增的地址响应给前端实现局部刷新
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        # 响应结果：需要将新的地址返回给前端渲染

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'address':address_dict})


class AddressView(LoginRequiredMixin, View):
    """用户收货地址"""
    def get(self, request):
        # 接受当前登录用户
        login_user = request.user
        # 查询当前等登录用户的地址信息 满足两个条件　当前登录　is_delete
        addresses = Address.objects.filter(user=login_user, is_deleted=False)
        # 将用户列表模型转换为字典列表， JsonResponse和vue.js 不认识模型类型，django魔板引擎认识
        address_list = []
        for address in addresses:
            address_dict = {
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }
            address_list.append(address_dict)

        # 构造上下文
        context = {
            'default_address_id': login_user.default_address_id or '0',
            # 'default_address_id': login_user.default_address_id,
            'addresses': address_list
        }
        return render(request, "user_center_site.html", context)


class VerifyEmailView(View):
    """验证邮箱"""

    def get(self, request):
        # 获取邮箱
        token = request.GET.get("token")
        # 检验邮箱
        if not token:
            return http.HttpResponseForbidden("缺少token")
        # 从token中获取user id email
        user = check_verify_email_token(token)
        if not user:
            return http.HttpResponseForbidden("无效的token")
        # 保存到数据库
        try:
            user.email_active = True
            user.save()
        except Exception as e:
            logger.error(e)
            return http.HttpResponseForbidden("激活邮箱失败")
        else:
            return redirect(reverse("users:info"))
            # 重定向到用户中心


class EmailView(View):
    """邮箱验证"""

    def put(self, request):
        json_str = request.body.decode() # byest 转成 str
        json_dict = json.loads(json_str)
        email = json_dict.get("email")

        # 校验参数
        if not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z0-9]{2,5}){1,2}$", email):
           return HttpResponseForbidden("参数email有误")

        # 将传入的数据保存到数据库邮箱
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({"code": RETCODE.DBERR, "errsmg": "添加邮箱失败"})

        # 发送验证邮箱逻辑
        email_url = generate_verify_email_url(request.user)
        send_verify_email.delay(email, email_url)

        # 响应结果
        return JsonResponse({"code": RETCODE.OK, "errmsg": "OK"})


class UserInfoView(LoginRequiredMixin, View):
    """用户中心"""
    def get(self, request):
        contentx ={
            "username": request.user.username,
            "mobile": request.user.mobile,
            "email": request.user.email,
            "email_active": request.user.email_active,
        }


        return render(request, "user_center_info.html", contentx)


class LogoutView(View):
    """用户退出"""
    def get(self, request):
        # 清除状态保持
        logout(request)
        # 重定向到首页
        response = redirect(reverse("contents:index"))
        # 删除cookie缓存
        response.delete_cookie("username")

        return response


class LoginView(View):
    def get(self, request):
        """返回登录页面"""
        return render(request, "login.html")

    def post(self, request):
        """提供登录页面的逻辑"""
        # 获取数据
        username = request.POST.get("username")
        password = request.POST.get("password")
        remembered = request.POST.get("remembered")

        # 检验数据
        if not all([username, password]):
            return HttpResponseForbidden("缺少必传参数")

        # 认证用户:使用账号查询用户是否存在，如果用户存在，再校验密码是否正确
        user = authenticate(username=username, password=password)  # 判断用户名及密码 
        if user is None:
            return render(request, "login.html", {"account_errmsg": "帐号或者密码错误"})

        # 状态保持
        login(request, user),
        # 使用remembered设置状态保持周期(实现记录周期)
        if remembered != 'on':
            # 没有记录登录，状态保持在退出浏览器就销毁
            request.session.set_expiry(0)   # 单位是秒
        else:
            # 状态保持默认是两周
            request.session.set_expiry(None)

        # 先取出next
        next = request.GET.get("next")
        if next:
            response = redirect(next)
        else:
            response = redirect(reverse("contents:index"))

        # 展示右上角信息，需要将用户名缓存到cookie中
        response.set_cookie("username", user.username, max_age=3600 * 24 * 14)

        response = marge_carts_cookie_redis(request, response, user)

        # 重定向到首页
        return response


class UsernameCountView(View):
    """ 验证用户名是否重复　"""
    def get(self, request, username):
        """
        :param username: 用户名
        :return: json
        """

        # 实现主题业务逻辑，使用username查询对应内容的条数(filter()是返回满足条件的结果集)
        count = Users.objects.filter(username=username).count()
        # 响应结果
        return JsonResponse({"code": RETCODE.OK, "errmsg": "OK", "count": count})


class MobileCountView(View):
    """验证手机号是否重复"""
    def get(self, request, mobile):

        count = Users.objects.filter(mobile=mobile).count()
        return JsonResponse({"code": RETCODE.OK, "errmsg": "OK", "count": count})


class RegisterView(View):

    def get(self,request):
        return render(request, 'register.html')

    def post(self,request):
        username = request.POST.get("username")
        password = request.POST.get("password")
        password2 = request.POST.get("password2")
        mobile = request.POST.get("mobile")
        allow = request.POST.get("allow")
        sms_code_client = request.POST.get("sms_code")
        # 判断接受的内容是否符合注册规则
        # 判断接受的内容是否为空
        if not all([username, password, password2, mobile, allow]):
            return HttpResponseForbidden("缺少必传参数")

        # 判断用户名
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return HttpResponseForbidden("请输入5-20个字符的用户名")

        # 判断密码
        if not re.match(r'^[a-zA-Z0-9_-]{8,20}$', password):
            return HttpResponseForbidden("请输入8-20位的密码")

        # 二次判断密码
        if password != password2:
            return HttpResponseForbidden("两次输入的密码不一致")

        # 判断手机号码
        if not re.match(r'^1[3-9]\d{9}$',mobile):
            return HttpResponseForbidden("您输入的手机格式不正确")

        # 判断协议
        if allow != 'on':
            return HttpResponseForbidden("请勾选用户协议")

        # 判断手机验证码是否输入正确
        redis_conn = get_redis_connection("verify_code")
        sms_code_server = redis_conn.get(mobile)
        sms_code_server = sms_code_server.decode()
        if redis_conn is None:
            return render(request, "register.html", {"error_mobile_code_message": "验证码失效"})
        if sms_code_client != sms_code_server:
            return render(request, "register.html", {"error_mobile_code_message": "验证码错误"})

        # 保存注册数据　是业务注册的核心！
        # 错误机制
        try:
            user = Users.objects.create_user(username=username, password=password, mobile=mobile)
        except DatabaseError:
            return render(request, 'register.html', {"register_message": "注册失败"})

        # 实现状态保持
        # 手动登录用户时，必须在调用login（）之前使用authenticate（）成功认证用户。
        user = authenticate(username=username, password=password)
        login(request, user)

        response = redirect(reverse("contents:index"))

        # 展示右上角信息，需要将用户名缓存到cookie中
        response.set_cookie("username", user.username, max_age=3600 * 24 * 14)

        response = marge_carts_cookie_redis(request, response, user)
        # 重定向到首页
        return response
